/**
 * @file Listener.h
 * @brief 
 * 
 *      初始化socket raw，并使用hio和channel封装，监听IO事件，以供处理
 *      channel->onwrite 写完成回调
 *      channel->onread  读回调
 * 
 * @author Zyan (zyishere2001@qq.com)
 * @version 1.0
 * @date 2022-05-08
 * 
 * @copyright Copyright (c) 2022  Zyanishere2001
 * 
 * @par 修改日志:
 * 
 */

#ifndef _LISTENER
#define _LISTENER


#include "EventLoopThread.h"
#include "Channel.h"
#include "JLog.h"

#include <iphlpapi.h>
#include <mstcpip.h>
#include <WinSock2.h>

using namespace hv;

class Listener{
public:
    Listener(EventLoopPtr loop ,const char* ip){
        this->_ip = ip;
        this->_loop = loop->loop();
        this->initSocket();
    }

    void startRecv(){
        assert(channel != NULL);
        if(channel->startRead()){
            printf("start Reading\n");
        }
    }

    inline void stop(){
        channel->stopRead();
        channel->close(true);
    }

    ChannelPtr channel;

private:
    void initSocket(){
    #ifndef OS_WIN  
        //hv封装的全平台创建raw socket的函数，理论上是都能用，但是不知道为什么windows不能读取,有空研究一下源码
        auto io = hio_create_socket(_loop,_ip,0,HIO_TYPE_SOCK_RAW,HIO_CLIENT_SIDE);
        //再封装一次
        channel.reset(new Channel(io));

    #else //windows下初始化
        WSAData wsaData;
        int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
        if (iResult != 0) {
            JLog::ERR(_loop,"WSAStartup failed:"+iResult, "seach for the err code");
        }

        SOCKET sd = socket(AF_INET, SOCK_RAW, IPPROTO_IP);
        if (sd == INVALID_SOCKET) {
            JLog::ERR(_loop,"socket() failed: %u" + WSAGetLastError(),"seach for the err code");
        }

	    // Captured IP packets sent and received by the network interface the
	    // specified IP address is associated with.
        struct sockaddr_in addr;
        addr.sin_family = AF_INET; //ipv4
        addr.sin_addr.s_addr = inet_addr(_ip); //ip
        addr.sin_port = htons(0); //port

        // Bind the socket to the specified IP address.
        int rc = bind(sd, (struct sockaddr*)&addr, sizeof(addr));
        if (rc == SOCKET_ERROR) {
            JLog::ERR(_loop,"bind() failed: failed: % u" + WSAGetLastError(), "seach for the err code");
        }
        // Give us ALL IPv4 packets sent and received to the specific IP address.
        int value = RCVALL_IPLEVEL;
        DWORD out = 0;
        rc = WSAIoctl(sd, SIO_RCVALL, &value, sizeof(value), NULL, 0, &out, NULL, NULL);
        if (rc == SOCKET_ERROR) {
            JLog::ERR(_loop,"WSAIoctl() failed: " + WSAGetLastError(), "seach for the err code");
        }

        //===========封装进入事件循环，（IO事件）============
        hio_t* io = hio_get(_loop,sd);
        channel.reset(new Channel(io));
    #endif

    }

private:
    hloop_t* _loop; //事件循环
    hio_t* io; // raw socket
    const char* _ip;

};

#endif